---
title: Testing
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import CodeBlock from "@theme/CodeBlock";
import testingOriginalTestFlutter from "!!raw-loader!/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart";
import testingOriginalTestDart from "!!raw-loader!/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart";
import repositorySnippet from "!!raw-loader!/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_repository.dart";
import testFlutter from "!!raw-loader!/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart";
import testDart from "!!raw-loader!/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart";
import testOverrideInfo from "!!raw-loader!/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_override_info.dart";
import testFull from "!!raw-loader!/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart";
import { trimSnippet } from "../../../../../src/components/CodeSnippet";

Per qualsiasi applicazione di media-grande scala è fondamentale testare l'applicazione.

Per testare con successo la nostra applicazione vorremo le seguenti cose:

- Nessuno stato dovrebbe essere preservato tra `test`/`testWidgets`.  
  Ciò significa che nessuno stato globale nell'applicazione o tutti gli stati 
  globali devono essere ripristinati dopo ogni test.

- Essere in grado di forzare i nostri provider ad avere uno stato specifico,
  mediante mocking o manipolandoli fino a raggiungere lo stato desiderato.
   
Vediamo caso per caso come [Riverpod] ti aiuta per i test.

## Nessuno stato dovrebbe essere preservato tra `test`/`testWidgets`. 

I provider sono generalmente dichiarati come variabili globali, 
questo potrebbe preoccuparti ed è comprensibile.
Dopotutto, lo stato globale rende i test molto difficili 
in quanto può richiedere lunghe funzioni `setUp`/`tearDown`.

Ma la realtà è diversa: mentre i provider sono dichiarati come globali,
lo stato di un provider **non** è globale.

Infatti, lo stato è salvato in un oggetto chiamato [ProviderContainer],
che potresti aver notato se hai visto gli esempi dart-only.
Se non è così, ti basta sapere che questo oggetto è creato implicitamente
da [ProviderScope], il widget che abilita [Riverpod] nel nostro progetto.

Concretamente, ciò significa che due `testWidgets` che usano i provider
non condividono nessuno stato.
In quanto tale, non c'è bisogno di nessun metodo `setUp`/`tearDown`.

Ma un esempio è sempre meglio di lunghe spiegazioni:

<Tabs
  defaultValue="testWidgets"
  values={[
    { label: 'testWidgets (Flutter)', value: 'testWidgets', },
    { label: 'test (Solo Dart)', value: 'test', },
  ]}
>
<TabItem value="testWidgets">

<CodeBlock>{trimSnippet(testingOriginalTestFlutter)}</CodeBlock>

</TabItem>
<TabItem value="test">

<CodeBlock>{trimSnippet(testingOriginalTestDart)}</CodeBlock>

</TabItem>
</Tabs>

Come puoi vedere, `counterProvider` è stato dichiarato come globale ma
nessuno stato è stato condiviso tra i test.
In quanto tale, non ci dobbiamo preoccuppare che i nostri test possano comportarsi in modo
differente se eseguiti in una cartella diversa, poichè funzionano completamente isolati.

## Sovrascrivere il comportamento di un provider durante i test.

Una comune applicazione reale può avere i seguenti oggetti:

- Una classe `Repository` che fornisce una semplice e 'type-safe' API per effettuare richieste HTTP.

- Un oggetto che gestisce lo stato dell'applicazione che usa `Repository`
  per effettuare richieste HTTP basate su diversi fattori.
  Questo oggetto potrebbe essere un `ChangeNotifier`, `Bloc` o anche un provider.

Usando [Riverpod], può essere rappresentato in questo modo:

<CodeBlock>{trimSnippet(repositorySnippet)}</CodeBlock>

In questa situazione, quando facciamo un unit/widget test, tipicamente vogliamo
sostituire la nostra istanza `Repository` con una falsa implementazione che ritorna
una risposta pre-definita al posto di fare una reale richiesta HTTP.

Vogliamo poi che il nostro `todoListProvider` o l'equivalente utilizzi l'implementazione
emulata (mocked) di `Repository`.

Per ottenere questo, possiamo usare il parametro `overrides` di [ProviderScope]/[ProviderContainer] 
per sovrascrivere il comportamento di `repositoryProvider`:

<Tabs
  defaultValue="ProviderScope"
  values={[
    { label: 'ProviderScope (Flutter)', value: 'ProviderScope', },
    { label: 'ProviderContainer (Solo Dart)', value: 'ProviderContainer', },
  ]}
>
<TabItem value="ProviderScope">

<CodeBlock>{trimSnippet(testFlutter)}</CodeBlock>

</TabItem>
<TabItem value="ProviderContainer">

<CodeBlock>{trimSnippet(testDart)}</CodeBlock>

</TabItem>
</Tabs>

Come puoi vedere dal codice evidenziato, [ProviderScope]/[ProviderContainer] permette
di sostituire l'implementazione di un provider con un comportamento diverso.

:::info
Alcuni provider espongono modi semplificati per sovrascrivere il loro comportamento.
Per esempio, [FutureProvider] permette di sovrascrivere il provider con un `AsyncValue`:

<CodeBlock>{trimSnippet(testOverrideInfo)}</CodeBlock>

:::

:::info
La sintassi per sovrascrivere un provider con il modificatore `family` è
leggermente diversa.

Se hai usato un provider in questo modo:

```dart
final response = ref.watch(myProvider('12345'));
```

Puoi sovrascrivere il provider come:

```dart
myProvider('12345').overrideWithValue(...));
```

:::

## Esempio completo di full widget test

Riassumendo, di seguito l'intero codice per il nostro Flutter test.

<CodeBlock>{trimSnippet(testFull)}</CodeBlock>

[riverpod]: https://github.com/rrousselgit/riverpod
[providerscope]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ProviderScope-class.html
[providercontainer]: https://pub.dev/documentation/riverpod/latest/riverpod/ProviderContainer-class.html
[futureprovider]: ../providers/future_provider
[zone]: https://api.flutter.dev/flutter/dart-async/Zone-class.html
